package com.woniuxy.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.alipay.api.domain.UserVo;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.woniuxy.conmon.Assert;
import com.woniuxy.conmon.enums.BusinessCode;
import com.woniuxy.conmon.enums.RedisConst;
import com.woniuxy.conmon.enums.SystemCode;
import com.woniuxy.conmon.enums.UserStatus;
import com.woniuxy.pojo.China;
import com.woniuxy.pojo.SysPermission;
import com.woniuxy.pojo.SysRole;
import com.woniuxy.pojo.User;
import com.woniuxy.pojo.dto.*;
import com.woniuxy.pojo.vo.SelectRoleVO;
import com.woniuxy.pojo.vo.UserVO;
import com.woniuxy.service.UserService;
import com.woniuxy.utils.JwtUtil;
import com.woniuxy.utils.RandomNumberUtil;
import com.woniuxy.utils.SmsUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Duration;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    UserService userService;

    @Autowired
    JwtUtil jwtUtil;


    @Autowired
    SmsUtil smsUtil;

    @Value("${spring.mail.username}")
    private String from;
    @Autowired
    JavaMailSender mailSender;

    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Autowired
    private SysRoleController sysRoleController;

    @Autowired
    private ChinaController chinaController;

    @Autowired
    private SysPermissionController sysPermissionController;


    //后台生成图形验证吗返回给前台
    @GetMapping("/fetchCaptcha")
    public ResponseEntity<?> fetchCaptcha() throws IOException {
        //        一.获取画笔，绘制一个显示区域
//        1.指定长度，宽度
        int width = 110,height = 45;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics graphics = image.getGraphics();
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0,0,width,height);
        StringBuilder stringBuilder = new StringBuilder();
        //5) 从字符数组中随机得到字符
        String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        char[] arr = characters.toCharArray();
        Random random = new Random();

        for (int i = 0; i < 4; i++) {
            int index = random.nextInt(arr.length);
            char c = arr[index];
            stringBuilder.append(c);
            graphics.setFont(new Font(null,Font.BOLD + Font.ITALIC,22));
            graphics.setColor(getColor());
            graphics.drawString(String.valueOf(c),5 + (i *23),25);
        }

        for (int i = 0; i < 8; i++) {
            graphics.setColor(getColor());
            int x1 = random.nextInt(width);
            int x2 = random.nextInt(width);
            int y1 = random.nextInt(width);
            int y2 = random.nextInt(width);
            graphics.drawLine(x1,x2,y1,y2);
        }

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ImageIO.write(image,"jpeg",byteArrayOutputStream);

        String key = RedisConst.USER_VALIDATE_CODE
                + ":" + UUID.randomUUID().toString()
                .replace("-", "");

        redisTemplate.opsForValue()
                .set(key,stringBuilder.toString(), Duration.ofMinutes(10));

        Base64.Encoder encoder = Base64.getEncoder();
        String base64Img = "data:image/jpeg;base64," + encoder.encodeToString(byteArrayOutputStream.toByteArray());
        //返回的数据包含验证码的key和图片
        Map<String, String> data = new HashMap<>();
        data.put("key", key);  //redis中键
        data.put("code", base64Img);  //图片
        //返回key和图片的base64的编码
        return ResponseEntity.ok(data);
    }
    //写一个方法随机获取颜色
    private Color getColor() {
        Random random = new Random();
        //红，绿，蓝取值0-255，随机生成 0-255之间数
        int r = random.nextInt(256);
        int g = random.nextInt(256);
        int b = random.nextInt(256);
        return new Color(r, g, b);
    }


    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginDTO dto, HttpServletRequest request){
        ResponseEntity responseEntity = userService.login(dto);
//        1.判断服务层返回的状态码200
        if ((responseEntity.getStatusCode() == HttpStatus.OK)) {
            User user = (User) responseEntity.getBody();
            return createTokenUser(user,request);
        }else {
            return responseEntity;
        }
    }

    /**
     * 生成Token，并且封装UserVo对象
     */
    private ResponseEntity<?> createTokenUser(User user, HttpServletRequest request) {
//        1.封装要存储的token信息
        HashMap<String, Object> map = new HashMap<>();
        map.put("id",user.getId());
        map.put("username",user.getUsername());
        map.put("ip",request.getRemoteAddr());



        //根据用户的id获取用户的权限码
        List<SysPermission> permissionsByUserId = sysPermissionController.permissionService.getPermissionsByUserId(user.getId());

        // 假设 permissions 是 List<SysPermission>
        List<String> permissionCodes = permissionsByUserId.stream()
                .map(SysPermission::getCode)
                .collect(Collectors.toList());

        map.put("permissions",permissionCodes);
        log.error("permission===>" +permissionCodes );

        //创建token
        String token = jwtUtil.createToken(map);

        //2.存储缓存一天
        String key = RedisConst.USER_TOKEN_PREFIX + ":" + user.getId();
        redisTemplate.opsForValue().set(key,token, Duration.ofDays(1));


        //3使用vo过滤敏感性息
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user,userVO);
        //4.将vo和token以响应头的方式返回

        return ResponseEntity.ok()
                .header("Authorization", token)
                //告知浏览器可以访问 Authorization 响应头，否则访问不了
                .header("Access-Control-Expose-Headers", "Authorization")
                .body(userVO);
    }

    @GetMapping("/sendTelCode/{tel}")
    public ResponseEntity<?> sendTelCode(@PathVariable String tel){
        String code  = RandomNumberUtil.getRandomNumber(4);
        log.info("验证码：{}",code);

//        String result = smsUtil.sendSms(tel, code);
        String result = "OK";
        Assert.error(!result.equals("OK"), BusinessCode.SMS_SEND_ERROR);
//        缓存验证码
        redisTemplate.opsForValue().set(RedisConst.SHORT_MESSAGE_SERVICE+":"+tel,code,Duration.ofSeconds(30));
        return ResponseEntity.ok("给"+maskPhoneNumber(tel)+ SystemCode.SUCCESS_SEND.getMessage());
    }
    private String maskPhoneNumber(String phone){
        return phone.substring(0,3)+"****"+phone.substring(7);
    }

    @PostMapping("/telLogin")
    public ResponseEntity<?> telLogin(@RequestBody LoginDTO loginDTO, HttpServletRequest request){
        //1.判断验证码
        String telephone = loginDTO.getPhone();
        //1.1判断验证码是否存在
        String code  = redisTemplate.opsForValue().get(RedisConst.SHORT_MESSAGE_SERVICE + ":" + telephone).toString();
        Assert.error(code == null,BusinessCode.INVALID_VERIFICATION);
        //1.2判断验证码是否正确
        Assert.error(!loginDTO.getCode().equals(code),BusinessCode.VERIFICATION_ERROR);

        //2.判断手机号是否存在
        User user = userService.findTel(telephone);
        if(user == null){
            //手机号不存在直接注册一个新账号
            User newUser = new User();
            newUser.setPhone(telephone);
            newUser.setUsername(telephone);

            //加密密码
            String hashpw = BCrypt.hashpw("123");
            newUser.setPassword(hashpw);
            newUser.setIsSys(1L);
            newUser.setNickname(telephone);
            newUser.setRealname(telephone);
            newUser.setStatus(UserStatus.ACTIVE.getKey());

            userService.save(newUser);

            UserVO userVO = new UserVO();
            //过滤封装到新的vo,复制到空的newUser中
            BeanUtils.copyProperties(newUser,userVO);

            //生成token

//            return ResponseEntity.status(HttpStatus.OK).body(userVO);
            return createTokenUser(newUser,request);

        }else{
            UserVO userVO = new UserVO();
            BeanUtils.copyProperties(user,userVO);
//            return ResponseEntity.status(HttpStatus.OK).body(userVO);
            return createTokenUser(user,request);
        }
    }

    @GetMapping("/getEmailCode/{email}")
    public ResponseEntity<?> getEmailCode(@PathVariable String email){

        //如果邮箱存在，则不进项注册
        Assert.error(userService.emailExists(email),BusinessCode.EMAIL_EXIST_ERROR);
        //使用MQ优化
        String code = RandomNumberUtil.getRandomNumber(6);
        String context = "验证码是：" + code + ",该验证码15分钟之内有效！";

        HashMap<String, String> map = new HashMap<>();
        map.put("from",from);
        map.put("to",email);
        map.put("subject","验证码");
        map.put("text",context);

        rabbitTemplate.convertAndSend("MyEmail.queue",map);
        redisTemplate.opsForValue().set(RedisConst.EMAIL_ADDRESS_PREFIX+":"+email,code,Duration.ofSeconds(30));

        return ResponseEntity.ok(SystemCode.EMAIL_SEND_SUCCESS.getMessage());
    }

    @PostMapping("/emailLogin")
    public ResponseEntity<?> emailLogin(@RequestBody LoginDTO loginDTO, HttpServletRequest request){
        //1.判断验证码
        String email = loginDTO.getEmail();
        //1.1判断验证码是否存在
        String code = redisTemplate.opsForValue().get(RedisConst.EMAIL_ADDRESS_PREFIX+":"+email).toString();
        Assert.error(code ==null ,BusinessCode.INVALID_VERIFICATION);

        //1.2判断验证码是否正确
        Assert.error(!loginDTO.getCode().equals(code),BusinessCode.VERIFICATION_ERROR);

        //2.判断邮箱是否存在
        User user = userService.findEmail(email);
        if(user == null){
            //使用邮箱注册一个新账号
            User newUser = new User();
            newUser.setEmail(email);
            newUser.setUsername(email);
            newUser.setIsSys(1L);

            //加密存储密码
            String hashpw = BCrypt.hashpw("123");
            newUser.setPassword(hashpw);

            newUser.setNickname(email);
            newUser.setRealname(email);
            newUser.setStatus(UserStatus.ACTIVE.getKey());

            userService.save(newUser);

            UserVO userVO = new UserVO();

            //过滤封装到新的vo复制到空的newUser中，
            BeanUtil.copyProperties(newUser,userVO);
//            return ResponseEntity.status(HttpStatus.OK).body(userVO);
            return createTokenUser(newUser,request);

        }else{
            UserVo userVo = new UserVo();
            BeanUtil.copyProperties(user,userVo);
//            return ResponseEntity.status(HttpStatus.OK).body(userVo);
            return createTokenUser(user,request);
        }
    }

    @PostMapping("/createNewAccount")
    public ResponseEntity<?> createNewAccount(@RequestBody RegisterDTO dto, HttpServletRequest request){
        User newUser = userService.createNewAccount(dto);
        return createTokenUser(newUser,request);
    }
    @GetMapping("/getEmailCode4Login/{email}")
    public ResponseEntity<?> getEmailCode4Login(@PathVariable String email){
        //使用MQ优化
        String code = RandomNumberUtil.getRandomNumber(6);
        String context = "验证码是：" + code + ",该验证码15分钟之内有效！";

        HashMap<String, String> map = new HashMap<>();
        map.put("from",from);
        map.put("to",email);
        map.put("subject","验证码");
        map.put("text",context);
        rabbitTemplate.convertAndSend("MyEmail.queue",map);
        redisTemplate.opsForValue().set(RedisConst.EMAIL_ADDRESS_PREFIX+":"+email,code,Duration.ofSeconds(30));

        return ResponseEntity.ok(SystemCode.EMAIL_SEND_SUCCESS.getMessage());
    }


    @PostMapping("/getAllSysUser/{pageNum}/{pageSize}")
    public ResponseEntity<?> getAllSysUser(@PathVariable Integer pageNum,
                                           @PathVariable Integer pageSize,
                                           @RequestBody(required = false) SysAccountDTO dto){
        Page<User> userPage = new Page<>(pageNum,pageSize);
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class);
        wrapper.eq(User::getIsSys,1);

        if(dto != null && dto.getUsername() != null && !dto.getUsername().equals("")){
            wrapper.like(User::getUsername,dto.getUsername());
        }
        if(dto != null && dto.getPhone() != null && !dto.getPhone().equals("")){
            wrapper.eq(User::getPhone,dto.getPhone());
        }
        if(dto != null && dto.getRole() != null && !dto.getRole().equals("")){
            wrapper.like(User::getRole,dto.getRole());
        }

        userService.page(userPage,wrapper);

        return ResponseEntity.ok(userPage);
    }

    @GetMapping("/getAllRoles")
    public ResponseEntity<?> getAllRoles(){

        List<SysRole> allRoles = sysRoleController.sysRoleService.getBaseMapper().selectList(null);

        List<SelectRoleVO> roleVOS = allRoles.stream()
                .map(role -> {
                    SelectRoleVO roleVO = new SelectRoleVO();
                    BeanUtils.copyProperties(role, roleVO);
                    return roleVO;
                }).collect(Collectors.toList());

        return ResponseEntity.ok(roleVOS);
    }

    @PostMapping("/removeAccount/{id}")
    public ResponseEntity<?> moveAccount(@PathVariable Integer id){
        Boolean success = userService.moveAccount(id);
        return ResponseEntity.ok(success);
    }

    @PostMapping("/batchRemove")
    public ResponseEntity<?> batchRemove(@RequestBody List<Integer> ids){
        Boolean success = userService.batchRemove(ids);
        return ResponseEntity.ok(success);
    }

    @PostMapping("/getAccount4Edit/{id}")
    public ResponseEntity<?> getAccount4Edit(@PathVariable Integer id){
        User user = userService.getById(id);
        return ResponseEntity.ok(user);
    }

    @PostMapping("/saveChangeUser/{userId}")
    public ResponseEntity<?> saveChangeUser(@PathVariable Integer userId,@RequestBody UserDTO dto){
        Boolean success = userService.saveChangeUser(userId,dto);
        return ResponseEntity.ok(success);
    }

    @PostMapping("/addAccount")
    public ResponseEntity<?> addAccount(@RequestBody UserDTO dto){
        Boolean success = userService.addAccount(dto);
        return ResponseEntity.ok(success);
    }

    @PostMapping("/getAllPreUser/{pageNum}/{pageSize}")
    public ResponseEntity<?> getAllPreUser(@PathVariable Integer pageNum,
                                           @PathVariable Integer pageSize,
                                           @RequestBody PreUserDTO dto){
        Page<User> userPage = new Page<>();
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class).eq(User::getIsSys, 0);

        if(dto != null && dto.getUsername() != null && !dto.getUsername().equals("")){
            wrapper.like(User::getUsername,dto.getUsername());
        }

        if(dto != null && dto.getNickname() != null && !dto.getNickname().equals("")){
            wrapper.like(User::getNickname,dto.getNickname());
        }

        if(dto != null && dto.getStartTime() != null && dto.getEndTime() != null){
            wrapper.between(User::getCreateTime,dto.getStartTime(),dto.getEndTime());
        }

        userService.page(userPage,wrapper);
        return ResponseEntity.ok(userPage);
    }

    @PostMapping("/removePreUser/{id}")
    public ResponseEntity<?> removePreUser(@PathVariable Integer id){
        Boolean success = userService.removePreUser(id);
        return ResponseEntity.ok(success);
    }

    @PostMapping("/batchRemPreUser")
    public ResponseEntity<?> batchRemPreUser(@RequestBody List<Integer> ids){
        Boolean success = userService.batchRemPreUser(ids);
        return ResponseEntity.ok(success);
    }

    @PostMapping("/getPreUserInfo/{userId}")
    public ResponseEntity<?> getPreUserInfo(@PathVariable Integer userId){

        User userInfo = userService.getById(userId);

        return ResponseEntity.ok(userInfo);
    }

    @GetMapping("/getAllRegion")
    public ResponseEntity<?> getAllRegion(){
//        LambdaQueryWrapper<China> wrapper = Wrappers.lambdaQuery(China.class);
//        wrapper.eq(China::getPid,0);
//        wrapper.ne(China::getId,0);


        List<China> allProvinces = chinaController.chinaService.getAddress(0,"");
        return ResponseEntity.ok(allProvinces);
    }

    @PostMapping("/editPreUserInfo/{userId}")
    public ResponseEntity<?> editPreUserInfo(@PathVariable Integer userId,
                                             @RequestBody EditPreUserInfoDTO dto){
        User oldUser = userService.getById(userId);
        BeanUtils.copyProperties(dto,oldUser);
        Boolean success = userService.update(oldUser,Wrappers.lambdaUpdate(User.class)
                .eq(User::getId,userId));
        return ResponseEntity.ok(success);
    }

    @GetMapping("/logout/{userId}")
    public ResponseEntity<?> logOut(@PathVariable Integer userId){
        redisTemplate.delete(RedisConst.USER_TOKEN_PREFIX+":"+userId);
        return ResponseEntity.ok(SystemCode.LOGOUT.getMessage());
    }





}
